/*
	Copyright (c) 1993, George Byrkit
				Software Professionals Limited
				1809 Saxon Street
				Ann Arbor, MI 48103
				(313)-663-1009

				ALL RIGHTS RESERVED

	THIS FILE MAY BE INCLUDED ROYALTY-FREE in any program as long as the
	above copyright notice also appears in the text of the program executable.

	I hereby certify that I did NOT examine the internals of the equivalent
	Windows (reg TM of MicroSoft) functions in order to create these
	approximately functionally equivalent routines.

	Differences are:
		I don't look for a windows installation directory.  If you use the
		non-private functions, the file 'win.ini' is looked for in the CURRENT
		DIRECTORY

	filename:	inifile.cpp

	purpose:	emulate windows API functions to access the ini files

	instructions for building:
		compile:
		link:
		other:

	change log:
	When		Version	Who		Why
	01-jan-93	1.0		ghb		created
*/

/*
		PRAGMAS
*/

/*
		INCLUDE FILES
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "inifile.h"

#ifndef WIN32
#define stricmp strcasecmp
#define strnicmp strncasecmp
#endif

/*
		LOCAL DEFINES AND MACROS
*/

/*
		LOCAL STRUCTURES AND TYPEDEFS
*/

/*
		FORWARD REFERENCES TO FUNCTIONS DEFINED IN THIS SOURCE FILE
*/

/*
		EXTERN FUNCTION AND DATA REFERENCES
*/

/*
		LOCAL DATA  (MAY BE GLOBAL OR STATIC)
*/
/*
	this string MUST be part of the executable file for the functions in
	this file to be used in a non-infringing way
*/
static char *pCWN =	"inifile code is Copyright (c) 1993, George Byrkit\n"
				"Software Professionals Limited\n"
				"1809 Saxon Street\n"
				"Ann Arbor, MI 48103\n"
				"(313)-663-1009\n"
				"ALL RIGHTS RESERVED\n";

/*
	Note that appname and keyname are treated case-insensitively.
*/

/*
	returns a pointer to the line that contains the desired string

	open for write opens a file from the beginning
*/

/*
	basic finding function for reads.

	see ScanForCopy for use with writes
*/
int ScanFor ( LPCSTR lpAppName, LPCSTR lpKeyName,
			LPSTR lpReturnedString, int nSize,
			FILE *pIniFile )
{
	int appLen = strlen ( lpAppName );
	int keyLen = strlen ( lpKeyName );
	char *rc;
    int keyFound = 0;
    int index;

	// find application name, if present
	do {
		rc = fgets ( lpReturnedString, nSize, pIniFile );
		// check for error, possibly EOF.
		if ( rc != lpReturnedString ) {
			// didn't find the application.  Return not found
			return 0;
		}
	} while ( lpReturnedString[0] != '['
		|| strnicmp ( lpAppName, &lpReturnedString[1], appLen )
		|| lpReturnedString[1+appLen] != ']' );

	// OK, we found the application section.  Now look for the keyword
	do {
		rc = fgets ( lpReturnedString, nSize, pIniFile );
		// check for error, possibly EOF.
		if ( rc != lpReturnedString ) {
			// didn't find the application.  Return not found
			return 0;
		}
		// at the end of an application.  Obviously we didn't find the keyword
		if ( lpReturnedString[0] == '[' ) {
			// didn't find the keyword.  Return not found
			return 0;
		}
		if ( strnicmp ( lpKeyName, lpReturnedString, keyLen ) == 0 ) {
		    // skip blanks, might differ from Windows behaviour
		    for ( index = keyLen; isblank(lpReturnedString[index]); index++ );
		    keyFound = (lpReturnedString[index] == '=');
		}
    } while (!keyFound);		
//	} while ( strnicmp ( lpKeyName, lpReturnedString, keyLen )
//		|| lpReturnedString[keyLen] != '=' );

	// OK, we found it, and it's in the current buffer.  Return success.
	// Success is the nonzero index into the return string of where
	// the result (after the equal sign) is located
//	return keyLen+1;
    return index+1;
}

/*
	basic finding function for writes.

	see ScanFor for use with reads
*/
int InsertSection ( LPCSTR lpAppName, LPCSTR lpKeyName,
			LPCSTR lpValue, FILE *pFile )
{
	return fprintf ( pFile, "[%s]\n%s=%s\n", lpAppName, lpKeyName, lpValue );
}

int InsertKey ( LPCSTR lpKeyName, LPCSTR lpValue, FILE *pFile )
{
	return fprintf ( pFile, "%s=%s\n", lpKeyName, lpValue );
}

/*
	return of zero means no more copy to do.
*/
int ScanForCopyInsert ( LPCSTR lpAppName, LPCSTR lpKeyName,
			LPCSTR lpValue,
			FILE *pIniFile, FILE *pIniFileCopy )
{
	int appLen = strlen ( lpAppName );
	int keyLen = strlen ( lpKeyName );
	char	lpReturnedString [256];
	char *rc;

	// find application name, if present
	do {
		rc = fgets ( lpReturnedString, 256, pIniFile );
		// check for error, possibly EOF.
		if ( rc != lpReturnedString ) {
			// didn't find the application.  Return not found
			InsertSection ( lpAppName, lpKeyName, lpValue, pIniFileCopy );
			return 0;
		}
		// copy to output
		fputs ( lpReturnedString, pIniFileCopy );
	} while ( lpReturnedString[0] != '['
		|| strnicmp ( lpAppName, &lpReturnedString[1], appLen )
		|| lpReturnedString[1+appLen] != ']' );

	// OK, we found the application section.  Now look for the keyword
	do {
		rc = fgets ( lpReturnedString, 256, pIniFile );
		// check for error, possibly EOF.
		if ( rc != lpReturnedString ) {
			// didn't find the application.  Return not found
			InsertKey ( lpKeyName, lpValue, pIniFileCopy );
			return 1;
		}
		// at the end of an application.  Obviously we didn't find the keyword
		if ( lpReturnedString[0] == '[' ) {
			InsertKey ( lpKeyName, lpValue, pIniFileCopy );
			// copy the line in the buffer, that we just read, that starts
			// a new section
			fputs ( lpReturnedString, pIniFileCopy );

			// didn't find the keyword.  Return not found
			return 1;
		}
		if ( !strnicmp ( lpKeyName, lpReturnedString, keyLen )
			&& lpReturnedString[keyLen] == '=' ) {
			// this is the place
			break;
		}
	} while ( fputs ( lpReturnedString, pIniFileCopy ) != EOF );

	// OK, we found it, and it's in the current buffer.  Replace what's after
	// the equal sign and up to any comment, with what's in the string we got
	// here to write in
	rc = strchr ( &lpReturnedString[keyLen+1], ';' );
	// we got a trailing comment.  Now back up over whitespace
	if ( rc ) {
		while ( isspace(*(rc-1)) ) {
			rc--;	// back up
		} 
		// note that rc usually includes a newline character
		if ( !strchr ( rc, '\n' ) ) {
			// must provide a newline char, one wasn't there
			fprintf ( pIniFileCopy, "%s=%s%s\n", lpKeyName, lpValue, rc );
		}
		else {
			fprintf ( pIniFileCopy, "%s=%s%s", lpKeyName, lpValue, rc );
		}
	}
	else {
		fprintf ( pIniFileCopy, "%s=%s\n", lpKeyName, lpValue );
	}

	// indicate probably more to copy
	return 1;
}

UINT    WINAPI GetPrivateProfileInt(LPCSTR lpAppName, LPCSTR lpKeyName,
			int nDefault,
			LPCSTR lpFileName)
{
	char returnedString[256];
	UINT	rc;

	FILE *pIniFile = fopen ( lpFileName, "rt" );
	if ( !pIniFile ) {
		return nDefault;
	}

	rc = ScanFor ( lpAppName, lpKeyName, returnedString, 256, pIniFile );
	if ( !rc ) {
		return nDefault;
	}

	// get integer value
	return (UINT) atol ( &returnedString[ rc ] );
}

int     WINAPI GetPrivateProfileString(LPCSTR lpAppName, LPCSTR lpKeyName,
			LPCSTR lpDefault, LPSTR lpReturnedString, int nSize,
			LPCSTR lpFileName)
{
	char returnedString[256];
	int	rc;
	int i;
	int len;
	char *p;

	FILE *pIniFile = fopen ( lpFileName, "rt" );
	if ( !pIniFile ) {
		strcpy ( lpReturnedString, lpDefault );
		return strlen ( lpDefault );
	}

	rc = ScanFor ( lpAppName, lpKeyName, returnedString, 256, pIniFile );
	if ( !rc ) {
		strcpy ( lpReturnedString, lpDefault );
		return strlen ( lpDefault );
	}

	// get string.  Copy out and back, to get rid of leading line info
	strcpy ( returnedString, &returnedString[ rc ] );
	// look for a double quote
	if ( returnedString[0] == '\"' ) {
		// find terminating quote
		p = strchr ( &returnedString[1], '\"' );
		p++;
		*p = '\0';	// string is terminated after end quote
	}
	else if ( NULL != ( p = strchr ( returnedString, ';' ) ) ) {
		// found a comment char
		// terminate string there.  Trailing whitespace trim will still work.
		*p = '\0';
	}

	// trim off trailing whitespace
	len = strlen ( returnedString );
	for ( i = len-1; i > 0; i-- ) {
		if ( isspace(returnedString[i]) ) {
			returnedString[i] = '\0';	// trim it
		}
		else {
			break;
		}
	}
	strncpy ( lpReturnedString, returnedString, nSize - 1 );
	lpReturnedString[nSize] = '\0';	// force null termination

	return strlen ( lpReturnedString );
}

BOOL    WINAPI WritePrivateProfileString(LPCSTR lpAppName, LPCSTR lpKeyName,
			LPCSTR lpValue,

			LPCSTR lpFileName)
{
	char tmpFileName[256];
	char tmpFileName2[256];
	char buffer[256];
	int	rc;

	FILE *pIniFileCopy;
	FILE *pIniFile = fopen ( lpFileName, "rt" );

	// if it doesn't exist, create it and insert this section
	if ( !pIniFile ) {
		pIniFile = fopen ( lpFileName, "wt" );
		if ( !pIniFile ) {
			return 0;	// failure
		}
	
		InsertSection ( lpAppName, lpKeyName, lpValue, pIniFile );
		fclose ( pIniFile );
		return 0;
	}

	// get a temporary file name that is unique
	strcpy ( tmpFileName, lpFileName );
	tmpFileName[strlen ( tmpFileName )-1] = '$';	// turn last char into $

	pIniFileCopy = fopen ( tmpFileName, "wt" );
	// if can't open a temporary file, fail
	if ( !pIniFileCopy ) {
		rc = errno;
		fclose ( pIniFile );
		return 0;
	}

	rc = ScanForCopyInsert ( lpAppName, lpKeyName, lpValue,
			pIniFile, pIniFileCopy );

	if ( rc ) {
		while ( !feof ( pIniFile ) ) {
			if ( buffer != fgets ( buffer, sizeof ( buffer ) -1, pIniFile ) ) {
				if ( !feof ( pIniFile ) ) {
					rc = errno;
					fclose ( pIniFile );
					fclose ( pIniFileCopy );
					remove ( tmpFileName );
					return 0;
				}
				else {
					// it is end of file.  All is copied.  Swap files
					break;
				}
			}
			// while writing to the copy file, if an error is encountered
			if ( fputs ( buffer, pIniFileCopy ) == EOF ) {
				// error while writing
				rc = errno;
				fclose ( pIniFile );
				fclose ( pIniFileCopy );
				remove ( tmpFileName );
				return 0;
			}
		} // end while not eof
	}

	// close the files
	fclose ( pIniFile );
	fclose ( pIniFileCopy );

	// form the name of what to rename the current file to.
	strcpy ( tmpFileName2, lpFileName );
	tmpFileName2[strlen ( tmpFileName )-1] = '$';	// turn last char into $
	tmpFileName2[strlen ( tmpFileName )-2] = '$';	// also nextlast char into $
	rc = rename ( lpFileName, tmpFileName2 );
	if ( rc ) {
		// error on rename. can't do it.  bail out
		rc = errno;
		remove ( tmpFileName );
		return 0;
	}

	rc = rename ( tmpFileName, lpFileName );
	if ( rc ) {
		// error on rename. can't do it.  bail out
		rc = errno;
		// rename back original file.  Pray for no error!
		rename ( tmpFileName2, lpFileName );
		remove ( tmpFileName );	// delete temp file
		return 0;
	}

	// all went OK.  Now just delete the temp file that is the original file
	remove ( tmpFileName2 );	// delete temp file that is original file

	return 1;
}

UINT    WINAPI GetProfileInt(LPCSTR lpAppName, LPCSTR lpKeyName,
			int nDefault)
{
	return GetPrivateProfileInt (lpAppName, lpKeyName, nDefault, "win.ini" );
}

int     WINAPI GetProfileString(LPCSTR lpAppName, LPCSTR lpKeyName,
			LPCSTR lpDefault, LPSTR lpReturnedString, int nSize)
{
	return GetPrivateProfileString (lpAppName, lpKeyName, lpDefault,
					lpReturnedString, nSize, "win.ini" );
}

BOOL    WINAPI WriteProfileString(LPCSTR lpAppName, LPCSTR lpKeyName,
			LPCSTR lpValue)
{
	return WritePrivateProfileString (lpAppName, lpKeyName, lpValue, "win.ini");
}

#ifdef	TEST
main ()
{
	char rv[256];
	GetPrivateProfileString ( "myapp2", "key2", "none", rv, 256, "myapp.ini" );

	printf ("Value of key %s in section %s is %s\n", "key2", "myapp2", rv );
}
#endif

